import { HttpException, HttpStatus, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { ClientService } from "src/modules/client/client.service";
import { CompanyService } from "src/modules/company/company.service";
import { QualityService } from "src/modules/quality/quality.service";
import { StatisticService } from "src/modules/statistics/statistic.service";
import { TypeService } from "src/modules/type/type.service";
import { UserService } from "src/modules/user/user.service";
import { dateNewFirst, itemCompanyCheck } from "src/utils/util";
import { CustomizeStatus } from "src/utils/status";
import { Repository } from "typeorm";
import { CreateOrderArrayDto } from "./dto/create-order-array";
import { CreateOrderDto } from "./dto/create-order.dto";
import { UpdateOrderDto } from "./dto/update-order.dto";
import { Order } from "./order.entity";
import { Company } from "src/modules/company/company.entity";
import { Client } from "src/modules/client/client.entity";
import { User } from "src/modules/user/user.entity";
import { DateRangeDto } from "src/utils/dto/date-range.dto";

@Injectable()
export class OrderService {
  constructor(
    @InjectRepository(Order)
    private readonly orderRepository: Repository<Order>,
    private readonly companyService: CompanyService,
    private readonly typeService: TypeService,
    private readonly qualityService: QualityService,
    private readonly usersService: UserService,
    private readonly clientService: ClientService,
    private readonly statisticService: StatisticService
  ) {}
  async findAll(): Promise<[Order[], number]> {
    const ans = await this.orderRepository.findAndCount({
      relations: ["client", "type", "quality"],
    });
    const orders = ans[0].sort(dateNewFirst);
    return [orders, ans[1]];
  }
  async findByCompany(company: string | Company): Promise<[Order[], number]> {
    const companyId = company instanceof Company ? company.id : company;
    const companyFound = await this.companyService.find(companyId);
    const ans = await this.orderRepository.findAndCount({
      where: { company: companyFound },
      relations: ["client", "type", "quality"],
    });
    ans[0].sort(dateNewFirst);
    return ans;
  }
  async findByClient(client: string | Client): Promise<[Order[], number]> {
    const clientId = client instanceof Client ? client.id : client;
    const clientFound = await this.clientService.find(clientId);
    const ans = await this.orderRepository.findAndCount({
      where: { client: clientFound },
      relations: ["client", "type", "quality", "deliveryItems"],
    });
    ans[0].sort(dateNewFirst);
    return ans;
  }
  /** 查找一条订单, 返回包括订单Company, Type, Quality, DeliveryItem, User, Client */
  find(order: string | Order): Promise<Order> {
    const id = order instanceof Order ? order.id : order;
    return this.orderRepository.findOneOrFail(id, {
      relations: [
        "company",
        "type",
        "quality",
        "deliveryItems",
        "user",
        "client",
      ],
    });
  }
  async count(companyId?: string): Promise<number> {
    if (companyId) {
      const company = await this.companyService.find(companyId);
      return this.orderRepository.count({ company });
    }
    return this.orderRepository.count();
  }
  async countByDate(dateRangeDto: DateRangeDto) {
    return;
  }
  async findLast(companyId?: string): Promise<Order> {
    if (companyId) {
      const company = await this.companyService.find(companyId);
      return this.orderRepository.findOne({
        where: { company },
        order: {
          date: "DESC",
        },
      });
    }
    return this.orderRepository.findOne({
      order: {
        date: "DESC",
      },
    });
  }
  async findLastDone(companyId?: string): Promise<Order> {
    if (companyId) {
      const company = await this.companyService.find(companyId);
      return this.orderRepository.findOne({
        where: { company, ifDone: true },
        order: {
          date: "DESC",
        },
      });
    }
    return this.orderRepository.findOne({
      where: {
        ifDone: true,
      },
      order: {
        date: "DESC",
      },
    });
  }
  async countDone(companyId?: string): Promise<number> {
    if (companyId) {
      const company = await this.companyService.find(companyId);
      return this.orderRepository.count({ company, ifDone: true });
    }
    return this.orderRepository.count();
  }
  findOneWithCompany(id: string): Promise<Order> {
    return this.find(id);
  }
  /** 创建一条订单 */
  async create(
    createOrderDto: CreateOrderDto,
    userId: string,
    companyId: string
  ): Promise<Order> {
    const order = new Order();
    const company = await this.companyService.find(companyId);
    const user = await this.usersService.find(userId);
    const client = await this.clientService.find(createOrderDto.clientId);
    const type = await this.typeService.find(createOrderDto.type);
    const quality = await this.qualityService.find(createOrderDto.quality);
    itemCompanyCheck(company, [client, type, quality]);
    Object.assign(order, createOrderDto, {
      company,
      type,
      quality,
      user,
      client,
      left: createOrderDto.quantity,
    });
    this.statisticService.appendOrder(company);
    return this.orderRepository.save(order);
  }

  /** 批量创建订单 */
  async insert(
    createOrderArrayDto: CreateOrderArrayDto,
    userId: string,
    companyId: string
  ) {
    const company = await this.companyService.find(companyId);
    const user = await this.usersService.find(userId);
    const client = await this.clientService.find(createOrderArrayDto.clientId);
    itemCompanyCheck(company, [client]);
    const orders: Order[] = await Promise.all(
      createOrderArrayDto.orders.map(async (item): Promise<Order> => {
        const order = new Order();
        const [type, quality] = await Promise.all([
          this.typeService.find(item.type),
          this.qualityService.find(item.quality),
        ]);
        itemCompanyCheck(company, [type, quality]);
        Object.assign(order, item, {
          company,
          type,
          quality,
          user,
          client,
          left: item.quantity,
        });
        return order;
      })
    );
    this.statisticService.appendOrder(company);
    return (await this.orderRepository.insert(orders)).identifiers;
  }
  async remove(order: string | Order): Promise<Order> {
    const id = order instanceof Order ? order.id : order;
    const orderToRemove = await this.orderRepository.findOne(id, {
      relations: ["deliveryItems"],
    });
    // 如果商品已经关闭或送货, 不允许进行删除;
    if (orderToRemove.deliveryItems.length > 0) {
      throw new HttpException(
        {
          message: "商品已送出, 无法移除",
          target: "deliveryItems",
          statusCode: CustomizeStatus.PRECONDITION_NEEDED,
        },
        HttpStatus.FORBIDDEN
      );
    }
    return await this.orderRepository.remove(orderToRemove);
  }
  async update(
    updateOrderDto: UpdateOrderDto,
    id: string,
    userId: string
  ): Promise<Order> {
    const order = await this.find(id);
    if (order.ifDone) {
      throw new HttpException(
        {
          message: "商品流程已结束, 被锁定",
          target: "done",
          statusCode: CustomizeStatus.PRECONDITION_NEEDED,
        },
        HttpStatus.FORBIDDEN
      );
    }
    if (updateOrderDto.quantity && updateOrderDto.quantity !== order.quantity) {
      order.newQuantity = updateOrderDto.quantity;
    }
    const user = await this.usersService.find(userId);
    // 只允许修改在UpdateOrderDto中定义的值.
    // 由于在updateOrderDto中存在有额外的不被允许修改的值, 因此不能使用 Object.assign
    for (const item of ["po", "no", "remark", "size", "price", "quantity"]) {
      order[item] = updateOrderDto[item];
    }
    Object.assign(order, { user });
    if (updateOrderDto.type) {
      order.type = await this.typeService.find(updateOrderDto.type);
    }
    if (updateOrderDto.quality) {
      order.quality = await this.qualityService.find(updateOrderDto.quality);
    }
    return this.orderRepository.save(order);
  }

  /** 修改order.done */
  async done(order: string | Order, userId: string) {
    order = order instanceof Order ? order : await this.find(order);
    if (order.left > 0) {
      throw new HttpException(
        {
          message: "商品未完成送货而提前结束",
          target: "done",
          statusCode: CustomizeStatus.PRECONDITION_NEEDED,
        },
        HttpStatus.FORBIDDEN
      );
    }
    order.ifDone = true;
    order.doneDate = new Date();
    order.doneDate.setUTCMilliseconds(0);
    order.user = await this.usersService.find(userId);
    return this.orderRepository.save(order);
  }

  /** 修改order.done */
  async unDone(order: string | Order, user: string | User) {
    order = order instanceof Order ? order : await this.find(order);
    user = user instanceof User ? user : await this.usersService.find(user);
    order.ifDone = false;
    order.doneDate = null;
    order.user = user;
    return this.orderRepository.save(order);
  }
}
